//
// FILE NAME: CIDLib_Enum.hpp
//
// AUTHOR: Dean Roddey
//
// CREATED: 09/08/2018
//
// COPYRIGHT: Charmed Quark Systems, Ltd @ 2019
//
//  This software is copyrighted by 'Charmed Quark Systems, Ltd' and
//  the author (Dean Roddey.) It is licensed under the MIT Open Source
//  license:
//
//  https://opensource.org/licenses/MIT
//
// DESCRIPTION:
//
//  This header contains supporting code for the IDL compiler's enumeration support.
//  This stuff needs to be somewhere that any CIDLib program would include. Only
//  facilities not used by the IDL compiler itself will use this stuff since only they
//  will have IDL generated enumerations.
//
//  To allow for generic code, and not to have to generate templatized code for the
//  many IDL generated enums, we use TInt4 values for the enum values. The code
//  generated by the IDL compiler will cast the values as appropriate.
//
//  We also implement a ForEach() mechanism for contiguous enums.
//
// CAVEATS/GOTCHAS:
//
// LOG:
//
//  $_CIDLib_Log_$
//
#pragma once


#pragma CIDLIB_PACK(CIDLIBPACK)

//
//  The IDL will declare globally one of these per IDL defined enum. That global ctor
//  will set up the enum object, with the exception of those enums that get their
//  _Text value from loading text resources. We fault those in upon first use.
//
class CIDLIBEXP TEnumMap
{
    public :
        // ------------------------------------------------------------------------
        //  Public class types
        // ------------------------------------------------------------------------
        enum class ETextVals
        {
            None
            , AltText
            , AltText2
            , BaseName
            , Name
            , Text

            , Count
        };

        //
        //  An array of these is statically initialized by the IDL compiler, and will
        //  passed to the actual map object to be used as his value map. This gets us
        //  around the problem of being able to statically initialize lists of different
        //  sizes.
        //
        //  The _Text slot of m_astrTextVals may be empty if it comes from loadable
        //  text. It will be faulted in as required.
        //
        struct TEnumValItem
        {
            tCIDLib::TInt4      m_i4Value;
            tCIDLib::TInt4      m_i4AltNumVal;
            tCIDLib::TMsgId     m_midLoad;
            TString             m_astrTextVals[tCIDLib::c4EnumOrd(ETextVals::Count)];
        };


        // -------------------------------------------------------------------
        //  Public static methods
        // -------------------------------------------------------------------
        static const TString& strFormatEnumTextVals
        (
            const   ETextVals   eVal
        );

        static tCIDLib::TVoid ThrowBadEnumVal
        (
            const   TString&                strTypeName
            , const tCIDLib::TInt4          i4Val
        );


        // -------------------------------------------------------------------
        //  Constructors and destructor
        // -------------------------------------------------------------------
        TEnumMap
        (
            const   TString&            strTypeName
            , const tCIDLib::TCard4     c4ValCount
            , const tCIDLib::TBoolean   bNonContig
            ,       TEnumValItem        aitemValues[]
            , const TFacility* const    pfacLoad
            , const tCIDLib::TInt4      i4NotFoundVal
        );

        TEnumMap(const TEnumMap&) = delete;

        ~TEnumMap();


        // -------------------------------------------------------------------
        //  Public operators
        // -------------------------------------------------------------------
        TEnumMap& operator=(const TEnumMap&) = delete;


        // -------------------------------------------------------------------
        //  Public, non-virtual methods
        // -------------------------------------------------------------------
        tCIDLib::TBoolean bIsValidEnum
        (
            const   tCIDLib::TInt4          i4ToFind
        );

        tCIDLib::TCard4 c4MapEnumVal
        (
            const tCIDLib::TInt4            i4ToFind
            , const tCIDLib::TBoolean       bThrowIfNot
        );


        tCIDLib::TCard4 c4MapEnumText
        (
            const TString&                  strToFind
            , const ETextVals               eTextVal
            , const tCIDLib::TBoolean       bThrowIfNot
        );

        tCIDLib::TInt4 eMapEnumAltNum
        (
            const   tCIDLib::TInt4          i4ToFind
            , const tCIDLib::TBoolean       bThrowIfNot
        );

        tCIDLib::TVoid FormatValues
        (
                    TString&                strToFille
            , const TString&                strPrefix
            , const tCIDLib::TCh            chSepChar
            , const ETextVals               eTextVal
        );

        tCIDLib::TInt4 i4MapEnumAltNum
        (
            const  tCIDLib::TInt4           i4EnumToFind
            , const tCIDLib::TBoolean       bThrowIfNot
        );

        tCIDLib::TInt4 i4MapEnumText
        (
            const   TString&                strToFind
            , const ETextVals               eTextVal
            , const tCIDLib::TBoolean       bThrowIfNot
        );

        const TString& strMapEnumVal
        (
            const   tCIDLib::TInt4          i4ToFind
            , const ETextVals               eTextVal
            , const tCIDLib::TBoolean       bThrowIfNot
        );


    private :
        // -------------------------------------------------------------------
        //  Private, non-virtual methods
        // -------------------------------------------------------------------
        tCIDLib::TVoid LoadResText();

        tCIDLib::TVoid ThrowBadEnumAltNum
        (
            const   TString&                strTypeName
            , const tCIDLib::TInt4          i4Val
        );

        tCIDLib::TVoid ThrowBadEnumText
        (
            const   TString&                strTypeName
            , const TString&                strVal
        );


        // -------------------------------------------------------------------
        //  Private data members
        //
        //  m_bNonContig
        //      The enum has non-contiguous values. So finding a value by its enum
        //      value requires a search, not an index operation.
        //
        //  m_c4ValCount
        //      The number of entries in m_pValues;
        //
        //  m_pfacLoad
        //      If the _Text value comes from loadable text, this will be the pointer
        //      to the facility to load from. It will be loaded and then this will be
        //      cleared so we don't try to do it again. If its inline, this will be null
        //      from the start.
        //
        //  m_aitemValues
        //      The IDL compiler generates a static initialized list of these and passes
        //      it to us in our ctor. We store it and it provides our enum value info.
        //      Doing it this way gets us around the issue of being to initialize the
        //      list statically. It will be different sizes for different enums
        //
        //  m_strTypeName
        //      The name of the enum type, for error messages and debugging.
        // -------------------------------------------------------------------
        tCIDLib::TBoolean       m_bNonContig;
        tCIDLib::TCard4         m_c4ValCount;
        tCIDLib::TInt4          m_i4NotFoundVal;
        TEnumValItem*           m_aeitemValues;
        const TFacility*        m_pfacLoad;
        const TString&          m_strTypeName;
};


//
//  A couple macros to get the min/max values for an enum by type. This is generally
//  useful, but definitely helps in that it works for both hand created and IDL
//  generated enums as long as they have the standard three magic values, without
//  any extra overhead.
//
#define eMinEnumVal(E) E##::Min
#define eMaxEnumVal(E) E##::Max
#define eEnumValCount(E) E##::Count

#define c4MinEnumVal(E) tCIDLib::TCard4(E##::Min)
#define c4MaxEnumVal(E) tCIDLib::TCard4(E##::Max)
#define c4EnumValCount(E) tCIDLib::TCard4(E##::Count)


//
//  Our 'for each' support for enums. It uses the above macros to generically get
//  from type name to min/max values, since templates cannot do that themselves.
//
namespace tCIDLib
{
    //
    //  A variation that always goes all the way through. It takes start/stop'
    //  values but defaults them since that is almost always what you want. But
    //  you can use this to do a range (though it starts getting wordy usually.)
    //
    template
    <
        typename E
        , E eStart = eMinEnumVal(E)
        , E eEnd = eMaxEnumVal(E)
        , typename IterCB = tCIDLib::TVoid (*IterCB)(const E)
    > tCIDLib::TVoid ForEachE(IterCB iterCB)
    {
        for (E eInd = eStart; eInd <= eEnd; eInd++)
            iterCB(eInd);
    }


    //
    //  A variation that can be broken out of, and returns the value it stopped on.
    //  If it goes to the end, the return will be the ::Count value.
    //
    template<typename E, typename IterCB = tCIDLib::TBoolean (*IterCB)(const E)>
    E eForEachE(IterCB iterCB)
    {
        E eInd = eMinEnumVal(E);
        for (; eInd <= eMaxEnumVal(E); eInd++)
        {
            if (!iterCB(eInd))
                break;
        }
        return eInd;
    }
}

#pragma CIDLIB_POPPACK

